﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WpfWordsLearner
{
	public abstract class DisposableBase : IDisposable
	{
		private int _disposeState;

		/// <summary>
		/// Gets a value indicating whether the container
		/// has not yet been disposed or is in the process of disposing.
		/// </summary>
		public bool IsLiving
		{
			get { return _disposeState == 0; }
		}

		/// <summary>
		/// Gets a value indicating whether the container has been disposed of.
		/// </summary>
		public bool IsDisposed
		{
			get { return _disposeState == 2; }
		}

		#region IDisposable Members
		/// <summary>
		/// Standard IDisposable 'Dispose' method.
		/// Triggers cleanup of this class and suppresses garbage collector usage.
		/// </summary>
		public void Dispose()
		{
			Dispose(true);

			// This object will be cleaned up by the Dispose method.
			// Therefore, you should call GC.SupressFinalize to take this object off the finalization queue
			// and prevent finalization code for this object from executing a second time.
			GC.SuppressFinalize(this);
		}
		#endregion

		public event EventHandler BeforeDispose;

		protected void AssertIsLiving()
		{
			if (!IsLiving)
				throw new ObjectDisposedException(ToString());
		}

		// Dispose(bool calledExplicitly) executes in two distinct scenarios.
		// If calledExplicitly equals true, the method has been called directly
		// or indirectly by a user's code. Managed and unmanaged resources can be disposed.
		// If calledExplicitly equals false, the method has been called by the
		// runtime from inside the finalizer and you should not reference
		// other objects. Only unmanaged resources can be disposed.
		private void Dispose(bool calledExplicitly)
		{
			// Synchronize disposal...
			if (System.Threading.Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0)
			{
				// For calledExplicitly, throw on errors.If by the GC (aka finalizer) don't throw, since it's ignored anyway and creates overhead.

				// Fire events first because some internals may need access.
				try
				{
					if (BeforeDispose != null)
					{
						BeforeDispose(this, EventArgs.Empty);
						BeforeDispose = null;
					}
				}
				catch (Exception eventBeforeDisposeException)
				{
					if (!calledExplicitly)
					{
						if (System.Diagnostics.Debugger.IsAttached)
							System.Diagnostics.Debug.Fail(eventBeforeDisposeException.Message);
					}
					else
						throw;
				}

				// Then do internal cleanup.
				try
				{
					OnDispose(calledExplicitly);
				}
				catch (Exception onDisposeException)
				{
					if (!calledExplicitly)
					{
						if (System.Diagnostics.Debugger.IsAttached)
							System.Diagnostics.Debug.Fail(onDisposeException.Message);
					}
					else
						throw;
				}

				System.Threading.Interlocked.Exchange(ref _disposeState, 2);
			}
		}

		protected virtual void OnDispose(bool calledExplicitly)
		{
			// override to provide cleanup
		}

		// Being called by the GC...
		~DisposableBase()
		{
			Dispose(false);
		}
	}

}
